﻿Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler
Imports System.Workflow.ComponentModel.Design
Imports System.Workflow.Runtime.DebugEngine

Namespace System.Workflow.Activities
    <SRCategory("Standard"), SRDescription("ConstrainedGroupActivityDescription"), ToolboxItem(GetType(ActivityToolboxItem)), ActivityValidator(GetType(ConditionedActivityGroupValidator)), WorkflowDebuggerStepping(WorkflowDebuggerSteppingOption.Concurrent), Designer(GetType(ConditionedActivityGroupDesigner), GetType(IDesigner)), ToolboxBitmap(GetType(ConditionedActivityGroup), "Resources.cag.png")> _
    Public NotInheritable Class ConditionedActivityGroup
        Inherits CompositeActivity
        Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs)
        ' Methods
        Public Sub New()
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
        End Sub

        Private Function AllChildrenQuiet(ByVal cag As ConditionedActivityGroup, ByVal context As ActivityExecutionContext) As Boolean
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim context2 As ActivityExecutionContext
            For Each context2 In context.ExecutionContextManager.ExecutionContexts
                If (Not cag.GetActivityByName(context2.Activity.QualifiedName, True) Is Nothing) Then
                    Return False
                End If
            Next
            Return True
        End Function

        Protected Overrides Function Cancel(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If ((Not Me.CAGState Is Nothing) AndAlso Not Me.Cleanup(Me, executionContext)) Then
                Return ActivityExecutionStatus.Canceling
            End If
            Return ActivityExecutionStatus.Closed
        End Function

        Friend Function Cleanup(ByVal cag As ConditionedActivityGroup, ByVal context As ActivityExecutionContext) As Boolean
            Dim cAGState As ConditionedActivityGroupStateInfo = cag.CAGState
            cAGState.Completed = True
            Dim flag As Boolean = False
            Dim childrenStats As Dictionary([Of] String, CAGChildStats) = cAGState.ChildrenStats
            Dim activity As Activity
            For Each activity In cag.EnabledActivities
                If (childrenStats.Item(activity.QualifiedName).State = CAGChildState.Pending) Then
                    childrenStats.Item(activity.QualifiedName).State = CAGChildState.Idle
                End If
                Dim context2 As ActivityExecutionContext = ConditionedActivityGroup.GetChildExecutionContext(context, activity, False)
                If (Not context2 Is Nothing) Then
                    Dim runtimeInitializedActivity As Activity = Me.GetRuntimeInitializedActivity(context, activity)
                    Select Case runtimeInitializedActivity.ExecutionStatus
                        Case ActivityExecutionStatus.Executing
                            context2.CancelActivity(runtimeInitializedActivity)
                            flag = True
                            [Continue]
                        Case ActivityExecutionStatus.Canceling, ActivityExecutionStatus.Faulting
                            flag = True
                            [Continue]
                        Case ActivityExecutionStatus.Closed
                            Me.CleanupChildAtClosure(context, runtimeInitializedActivity)
                            [Continue]
                    End Select
                    activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
                    [Continue] For
                End If
            Next
            If Not flag Then
                context.CloseActivity
            End If
            Return Not flag
        End Function

        Private Sub CleanupChildAtClosure(ByVal context As ActivityExecutionContext, ByVal childActivity As Activity)
            childActivity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
            Dim childContext As ActivityExecutionContext = ConditionedActivityGroup.GetChildExecutionContext(context, childActivity, False)
            context.ExecutionContextManager.CompleteExecutionContext(childContext)
        End Sub

        Private Function EvaluateChildConditions(ByVal cag As ConditionedActivityGroup, ByVal child As Activity, ByVal context As ActivityExecutionContext) As Boolean
            Dim flag As Boolean
            Dim cAGState As ConditionedActivityGroupStateInfo = cag.CAGState
            Try 
                cAGState.Testing = True
                Dim condition As ActivityCondition = DirectCast(child.GetValue(ConditionedActivityGroup.WhenConditionProperty), ActivityCondition)
                flag = If((Not condition Is Nothing), condition.Evaluate(child, context), (cAGState.ChildrenStats.Item(child.QualifiedName).ExecutedCount = 0))
            Finally
                cAGState.Testing = False
            End Try
            Return flag
        End Function

        Friend Function EvaluateConditions(ByVal cag As ConditionedActivityGroup, ByVal context As ActivityExecutionContext) As Boolean
            If cag.CAGState.Completed Then
                Return False
            End If
            If ((cag.UntilCondition Is Nothing) OrElse Not cag.UntilCondition.Evaluate(cag, context)) Then
                Dim num As Integer = 0
                Dim childrenStats As Dictionary([Of] String, CAGChildStats) = cag.CAGState.ChildrenStats
                Dim activity As Activity
                For Each activity In cag.EnabledActivities
                    If (childrenStats.Item(activity.QualifiedName).State = CAGChildState.Excuting) Then
                        num += 1
                    Else
                        Dim runtimeInitializedActivity As Activity = Me.GetRuntimeInitializedActivity(context, activity)
                        If Me.EvaluateChildConditions(cag, runtimeInitializedActivity, context) Then
                            num += 1
                            childrenStats.Item(activity.QualifiedName).State = CAGChildState.Pending
                        End If
                    End If
                Next
                If (num > 0) Then
                    Return False
                End If
                If (Not cag.UntilCondition Is Nothing) Then
                    Throw New InvalidOperationException(SR.GetString("Error_CAGQuiet", New Object() { cag.QualifiedName }))
                End If
            End If
            Return True
        End Function

        Protected Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Me.CAGState = New ConditionedActivityGroupStateInfo(Me)
            If Me.EvaluateConditions(Me, executionContext) Then
                Return ActivityExecutionStatus.Closed
            End If
            Me.TriggerChildren(Me, executionContext)
            Return MyBase.ExecutionStatus
        End Function

        Private Sub ExecuteChild(ByVal cag As ConditionedActivityGroup, ByVal childActivity As Activity, ByVal context As ActivityExecutionContext)
            Dim context2 As ActivityExecutionContext = ConditionedActivityGroup.GetChildExecutionContext(context, childActivity, True)
            cag.CAGState.ChildrenStats.Item(childActivity.QualifiedName).State = CAGChildState.Excuting
            context2.Activity.RegisterForStatusChange(Activity.ClosedEvent, Me)
            context2.ExecuteActivity(context2.Activity)
        End Sub

        Public Function GetChildActivityExecutedCount(ByVal child As Activity) As Integer
            If (child Is Nothing) Then
                Throw New ArgumentNullException("child")
            End If
            Dim cAGState As ConditionedActivityGroupStateInfo = Me.CAGState
            If (cAGState Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_CAGNotExecuting", New Object() { MyBase.QualifiedName }))
            End If
            If Not cAGState.ChildrenStats.ContainsKey(child.QualifiedName) Then
                Throw New ArgumentException(SR.GetString("Error_CAGChildNotFound", New Object() { child.QualifiedName, MyBase.QualifiedName }), "child")
            End If
            Return cAGState.ChildrenStats.Item(child.QualifiedName).ExecutedCount
        End Function

        Private Shared Function GetChildExecutionContext(ByVal context As ActivityExecutionContext, ByVal childActivity As Activity, ByVal createIfNotExists As Boolean) As ActivityExecutionContext
            Dim executionContextManager As ActivityExecutionContextManager = context.ExecutionContextManager
            Dim executionContext As ActivityExecutionContext = executionContextManager.GetExecutionContext(childActivity)
            If ((executionContext Is Nothing) AndAlso createIfNotExists) Then
                executionContext = executionContextManager.CreateExecutionContext(childActivity)
            End If
            Return executionContext
        End Function

        Public Function GetDynamicActivity(ByVal childActivityName As String) As Activity
            If (childActivityName Is Nothing) Then
                Throw New ArgumentNullException("childActivityName")
            End If
            Dim childActivity As Activity = Nothing
            Dim i As Integer
            For i = 0 To MyBase.EnabledActivities.Count - 1
                If MyBase.EnabledActivities.Item(i).QualifiedName.Equals(childActivityName) Then
                    childActivity = MyBase.EnabledActivities.Item(i)
                    Exit For
                End If
            Next i
            If (childActivity Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_CAGChildNotFound", New Object() { childActivityName, MyBase.QualifiedName }), "childActivityName")
            End If
            Return Me.GetDynamicActivity(childActivity)
        End Function

        <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(False)> _
        Private Function GetDynamicActivity(ByVal childActivity As Activity) As Activity
            If (childActivity Is Nothing) Then
                Throw New ArgumentNullException("childActivity")
            End If
            If Not MyBase.EnabledActivities.Contains(childActivity) Then
                Throw New ArgumentException(SR.GetString("Error_CAGChildNotFound", New Object() { childActivity.QualifiedName, MyBase.QualifiedName }), "childActivity")
            End If
            Dim dynamicActivities As Activity() = MyBase.GetDynamicActivities(childActivity)
            If (dynamicActivities.Length <> 0) Then
                Return dynamicActivities(0)
            End If
            Return Nothing
        End Function

        Private Function GetRuntimeInitializedActivity(ByVal context As ActivityExecutionContext, ByVal childActivity As Activity) As Activity
            Dim context2 As ActivityExecutionContext = ConditionedActivityGroup.GetChildExecutionContext(context, childActivity, False)
            If (context2 Is Nothing) Then
                Return childActivity
            End If
            Return context2.Activity
        End Function

        Public Shared Function GetWhenCondition(ByVal dependencyObject As Object) As Object
            If (dependencyObject Is Nothing) Then
                Throw New ArgumentNullException("dependencyObject")
            End If
            If Not TypeOf dependencyObject Is DependencyObject Then
                Throw New ArgumentException(SR.GetString("Error_UnexpectedArgumentType", New Object() { GetType(DependencyObject).FullName }), "dependencyObject")
            End If
            Return [TryCast](dependencyObject,DependencyObject).GetValue(ConditionedActivityGroup.WhenConditionProperty)
        End Function

        Friend Sub HandleEvent(ByVal context As ActivityExecutionContext, ByVal e As SubscriptionEventArg)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            If (e Is Nothing) Then
                Throw New ArgumentNullException("e")
            End If
            Dim activity As ConditionedActivityGroup = [TryCast](context.Activity,ConditionedActivityGroup)
            If (activity Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_InvalidCAGActivityType"), "activity")
            End If
            If (activity.ExecutionStatus <> ActivityExecutionStatus.Closed) Then
                Dim subscriptionType As EventType = e.SubscriptionType
                Dim args As ActivityExecutionStatusChangedEventArgs = DirectCast(e.Args, ActivityExecutionStatusChangedEventArgs)
                Dim flag As Boolean = False
                Dim childrenStats As Dictionary([Of] String, CAGChildStats) = activity.CAGState.ChildrenStats
                If childrenStats.ContainsKey(args.Activity.QualifiedName) Then
                    If (args.ExecutionStatus <> ActivityExecutionStatus.Executing) Then
                        childrenStats.Item(args.Activity.QualifiedName).State = CAGChildState.Idle
                    End If
                    If (args.ExecutionStatus = ActivityExecutionStatus.Closed) Then
                        Dim local1 As CAGChildStats = childrenStats.Item(args.Activity.QualifiedName)
                        local1.ExecutedCount += 1
                    End If
                    Try 
                        If (activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                            flag = Me.EvaluateConditions(activity, context)
                        End If
                    Finally
                        Me.CleanupChildAtClosure(context, args.Activity)
                    End Try
                ElseIf (activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                    flag = Me.EvaluateConditions(activity, context)
                End If
                If flag Then
                    Me.Cleanup(activity, context)
                ElseIf activity.CAGState.Completed Then
                    If Me.AllChildrenQuiet(activity, context) Then
                        context.CloseActivity
                    End If
                Else
                    Me.TriggerChildren(activity, context)
                End If
            End If
        End Sub

        Protected Overrides Sub OnActivityChangeAdd(ByVal executionContext As ActivityExecutionContext, ByVal addedActivity As Activity)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (addedActivity Is Nothing) Then
                Throw New ArgumentNullException("addedActivity")
            End If
            If addedActivity.Enabled Then
                Dim activity As ConditionedActivityGroup = [TryCast](executionContext.Activity,ConditionedActivityGroup)
                Dim cAGState As ConditionedActivityGroupStateInfo = activity.CAGState
                If ((activity.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso (Not cAGState Is Nothing)) Then
                    cAGState.ChildrenStats.Item(addedActivity.QualifiedName) = New CAGChildStats
                End If
            End If
        End Sub

        Protected Overrides Sub OnActivityChangeRemove(ByVal executionContext As ActivityExecutionContext, ByVal removedActivity As Activity)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (removedActivity Is Nothing) Then
                Throw New ArgumentNullException("removedActivity")
            End If
            If removedActivity.Enabled Then
                Dim activity As ConditionedActivityGroup = [TryCast](executionContext.Activity,ConditionedActivityGroup)
                Dim cAGState As ConditionedActivityGroupStateInfo = activity.CAGState
                If ((activity.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso (Not cAGState Is Nothing)) Then
                    cAGState.ChildrenStats.Remove(removedActivity.QualifiedName)
                End If
            End If
        End Sub

        Protected Overrides Sub OnClosed(ByVal provider As IServiceProvider)
            MyBase.RemoveProperty(ConditionedActivityGroup.CAGStateProperty)
        End Sub

        Protected Overrides Sub OnWorkflowChangesCompleted(ByVal executionContext As ActivityExecutionContext)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Dim activity As ConditionedActivityGroup = [TryCast](executionContext.Activity,ConditionedActivityGroup)
            If (activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                Dim cAGState As ConditionedActivityGroupStateInfo = activity.CAGState
                If ((Not cAGState Is Nothing) AndAlso Not cAGState.Testing) Then
                    If Me.EvaluateConditions(activity, executionContext) Then
                        Me.Cleanup(activity, executionContext)
                    Else
                        Me.TriggerChildren(activity, executionContext)
                    End If
                End If
            End If
        End Sub

        Public Shared Sub SetWhenCondition(ByVal dependencyObject As Object, ByVal value As Object)
            If (dependencyObject Is Nothing) Then
                Throw New ArgumentNullException("dependencyObject")
            End If
            If Not TypeOf dependencyObject Is DependencyObject Then
                Throw New ArgumentException(SR.GetString("Error_UnexpectedArgumentType", New Object() { GetType(DependencyObject).FullName }), "dependencyObject")
            End If
            [TryCast](dependencyObject,DependencyObject).SetValue(ConditionedActivityGroup.WhenConditionProperty, value)
        End Sub

        Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.ActivityExecutionStatusChangedEventArgs>.OnEvent(ByVal sender As Object, ByVal e As ActivityExecutionStatusChangedEventArgs) Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs).OnEvent
            Me.HandleEvent([TryCast](sender,ActivityExecutionContext), New SubscriptionEventArg(e, EventType.StatusChange))
        End Sub

        Friend Sub TriggerChildren(ByVal cag As ConditionedActivityGroup, ByVal context As ActivityExecutionContext)
            Dim childrenStats As Dictionary([Of] String, CAGChildStats) = cag.CAGState.ChildrenStats
            Dim activity As Activity
            For Each activity In cag.EnabledActivities
                If (childrenStats.Item(activity.QualifiedName).State = CAGChildState.Pending) Then
                    Dim runtimeInitializedActivity As Activity = Me.GetRuntimeInitializedActivity(context, activity)
                    If (runtimeInitializedActivity.ExecutionStatus = ActivityExecutionStatus.Initialized) Then
                        Me.ExecuteChild(cag, runtimeInitializedActivity, context)
                    End If
                End If
            Next
        End Sub


        ' Properties
        Friend Property CAGState As ConditionedActivityGroupStateInfo
            Get
                Return DirectCast(MyBase.GetValue(ConditionedActivityGroup.CAGStateProperty), ConditionedActivityGroupStateInfo)
            End Get
            Set(ByVal value As ConditionedActivityGroupStateInfo)
                MyBase.SetValue(ConditionedActivityGroup.CAGStateProperty, value)
            End Set
        End Property

        <DefaultValue(CStr(Nothing)), SRCategory("Conditions"), SRDescription("UntilConditionDescr")> _
        Public Property UntilCondition As ActivityCondition
            Get
                Return [TryCast](MyBase.GetValue(ConditionedActivityGroup.UntilConditionProperty),ActivityCondition)
            End Get
            Set(ByVal value As ActivityCondition)
                MyBase.SetValue(ConditionedActivityGroup.UntilConditionProperty, value)
            End Set
        End Property


        ' Fields
        Private Shared CAGStateProperty As DependencyProperty = DependencyProperty.Register("CAGState", GetType(ConditionedActivityGroupStateInfo), GetType(ConditionedActivityGroup))
        Public Shared ReadOnly UntilConditionProperty As DependencyProperty = DependencyProperty.Register("UntilCondition", GetType(ActivityCondition), GetType(ConditionedActivityGroup), New PropertyMetadata(DependencyPropertyOptions.Metadata))
        Public Shared ReadOnly WhenConditionProperty As DependencyProperty = DependencyProperty.RegisterAttached("WhenCondition", GetType(ActivityCondition), GetType(ConditionedActivityGroup), New PropertyMetadata(DependencyPropertyOptions.Metadata), GetType(WhenUnlessConditionDynamicPropertyValidator))

        ' Nested Types
        Private NotInheritable Class WhenUnlessConditionDynamicPropertyValidator
            Inherits Validator
            ' Methods
            Public Overrides Function Validate(ByVal manager As ValidationManager, ByVal obj As Object) As ValidationErrorCollection
                Dim errors As ValidationErrorCollection = ValidationHelpers.ValidateObject(manager, obj)
                If (errors.Count = 0) Then
                    Dim context As Activity = [TryCast](manager.Context.Item(GetType(Activity)),Activity)
                    If (context Is Nothing) Then
                        Throw New InvalidOperationException(SR.GetString("Error_ContextStackItemMissing", New Object() { GetType(Activity).Name }))
                    End If
                    Dim condition As CodeCondition = [TryCast](obj,CodeCondition)
                    If ((Not condition Is Nothing) AndAlso condition.IsBindingSet(CodeCondition.ConditionEvent)) Then
                        Dim binding As ActivityBind = condition.GetBinding(CodeCondition.ConditionEvent)
                        If (Not binding Is Nothing) Then
                            Dim activity As Activity = Helpers.ParseActivityForBind(context, binding.Name)
                            If ((Not activity Is Nothing) AndAlso Helpers.IsChildActivity(context.Parent, activity)) Then
                                Dim fullPropertyName As String = MyBase.GetFullPropertyName(manager)
                                Dim item As New ValidationError(SR.GetString("Error_NestedConstrainedGroupConditions", New Object() { fullPropertyName }), &H615) { _
                                    .PropertyName = fullPropertyName _
                                }
                                errors.Add(item)
                            End If
                        End If
                    End If
                End If
                Return errors
            End Function

        End Class
    End Class
End Namespace

